/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package asis;

import asis.ASISSaveFile.GUISettings;
import asis.ini.INI;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import skyproc.*;
import skyproc.gui.SPProgressBarPlug;

/**
 *
 * @author pc tech
 */
public class IncreasedSpawns {

    public void addSpawns() {
        internalAddSpawns();
    }

    public void addSpawns(ASIS a) {
        if (a.getModExclusions() != null) {
            modExclusions.addAll(a.getModExclusions());
        }
        internalAddSpawns();
    }

    private void internalAddSpawns() {
        SPProgressBarPlug.setStatus("Increased Spawns: Starting Patch");
        SPProgressBarPlug.incrementBar();
        Mod merger = new Mod("TmpMerger", false);
        merger.addAsOverrides(SPGlobal.getDB());
        //Processes the ini files and checks them for the patch settings.
        processINI();
        //getPatchSettings();
//        getBlockList();
        setValidNPCs(merger);
        addIncreasedSpawns(merger);
    }

    private void setValidNPCs(Mod merger) {
        validNPCs = new LinkedHashSet<>(merger.getNPCs().getRecords());

        //Clean-up NPC's from excluded mods.  This is done as blocking the import 
        // could cause previous NPC's to override an excluded mods NPC's.

        for (String list : modExclusions) {
            try {
                Mod tempMod = SPDatabase.getMod(new ModListing(list));
                validNPCs.removeAll(tempMod.getNPCs().getRecords());
            } catch (Exception e) {
                //Do nothing, probably doesn't have the mod in the exclusions.
            }
        }

        ArrayList<NPC_> badNPCs = new ArrayList<>();
        for (NPC_ n : validNPCs) {
            if (!getValidNPC(n)) {
                badNPCs.add(n);
            }
        }
        for (NPC_ n : badNPCs) {
            validNPCs.remove(n);
        }
    }

    private void addIncreasedSpawns(Mod merger) {
        //FormID dupNPCForm = null;
        String scriptName = "increasedSpawns";
        int numMaxSpawns;
        float numMult;
        Mod patch = SPGlobal.getGlobalPatch();

        FLST listOfInvalidCells = null;
        FLST listOfValidNPCs = null;
        FormID uniqueFaction = null;
        FormID intspawns = null;
        FormID[] globals = new FormID[10];

        try {
            listOfValidNPCs = new FLST("ASIS_ListOfValidNPCsIncreasedSpawns");
            listOfInvalidCells = new FLST("ASIS_listOfInvalidCellsIncreasedSpawns");
            uniqueFaction = new FormID("000800", SPDatabase.getMod(new ModListing("ASIS-Dependency", false)).getInfo());

            GLOB intSpawnsGLOB = (GLOB) merger.getMajor("ASISIntSpawns", GRUP_TYPE.GLOB);

            intSpawnsGLOB.setValue(ASIS.save.getFloat(GUISettings.ISREDUCEDINTERIORSPAWNS));
            intspawns = intSpawnsGLOB.getForm();
            patch.addRecord(intSpawnsGLOB);

            for (int i = 0; i < 10; i++) {
                GLOB SpawnWeightGLOB = (GLOB) merger.getMajor("ASISSpawnWeight" + i, GRUP_TYPE.GLOB);
                SpawnWeightGLOB.setValue(spawnweight[i]);
                globals[i] = SpawnWeightGLOB.getForm();
                patch.addRecord(SpawnWeightGLOB);
            }
        } catch (NullPointerException exception) {
            System.err.println(exception.toString());
            SPGlobal.logException(exception);
            SPGlobal.flush();
            JOptionPane.showMessageDialog(null, "The patcher will not work correctly if ASIS-Dependency.esp is not\ninstalled before running the patcher.  Please install the plugin\n(and check that it has not been ghosted by Wrye Bash) and try\nagain.  If you have done so and continue to see this message,\nplease contact the author.  The patcher will now exit.");
            System.exit(0);
        }

        SPProgressBarPlug.setStatus("Increased Spawns: Adding Spawns/Duplicating NPCs");
        SPProgressBarPlug.incrementBar();

        listOfInvalidCells.addAll(cellExclusions);


        // Changed Floats to Doubles
        float maxNumRandom = 0.0f;
        for (float currentNum : spawnweight) {
            maxNumRandom += currentNum;
        }
        FormID maxNumForm = (new GLOB("ASISMaxNum", GLOB.GLOBType.Float, maxNumRandom, true).getForm());

        for (NPC_ n : validNPCs) {
            try {
                //Returns the formID of the dup for addition to the NPC below.
                //dupNPCForm = duplicateNPCForSpawns(n);
                //duplicateNPCForTemplate(n);

                String edid = n.getEDID();

                listOfValidNPCs.addFormEntry(n.getForm());

                //Determines any unique NPC maximum spawn settings.
                numMaxSpawns = getNumMaxSpawns(n);
                FormID maxSpawnsForm = (new GLOB("ASISMaxNumSpawns" + edid, GLOB.GLOBType.Float, numMaxSpawns, true)).getForm();
                numMult = getNumReducedSpawns(n) * getNumIncreasedSpawns(n);
                FormID multForm = (new GLOB("ASISSpawnMult" + edid, GLOB.GLOBType.Float, numMult, true)).getForm();

                //Actual script addition is done here.
                ScriptRef script = new ScriptRef(scriptName);
                //script.setProperty("creatureToSpawn", dupNPCForm);
                script.setProperty("listOfValidNPCs", listOfValidNPCs.getForm());
                script.setProperty("listOfValidCells", listOfInvalidCells.getForm());
                script.setProperty("uniqueSpawnFaction", uniqueFaction);
                //script.setProperty("creatureToSpawn", n.getForm());
                script.setProperty("reducedInteriorSpawns", reducedInteriorSpawns);

                script.setProperty("isAmbushSpawn", n.getEDID().toUpperCase().contains("AMBUSH"));

//                    script.setProperty("numMaxSpawns", numMaxSpawns);
                for (int i = 0; i < 10; i++) {
                    script.setProperty("ASISSpawnWeight" + i, (globals[i]));
//                        script.setProperty("spawn" + i + "weight", spawnweight[i]);
                }
                script.setProperty("ASISIntSpawns", intspawns);
                script.setProperty("ASISMaxNumSpawns", maxSpawnsForm);
//                    script.setProperty("multiplierG", (new GLOB(SPGlobal.getGlobalPatch(), "ASISNumMult" + edid, 
//                        GLOB.GLOBType.Float, numMult, true)).getForm());
                script.setProperty("ASISSpawnMult", multForm);
                script.setProperty("ASISMaxNum", maxNumForm);
                n.getScriptPackage().addScript(script);
                patch.addRecord(n);

            } catch (Exception e) {
                SPGlobal.logException(e);
            }

        }
        //Add the NPC to the patch.
        patch.addRecord(listOfValidNPCs);
    }

    private void processINI() {

        SPProgressBarPlug.setStatus("Increased Spawns: Processing INI");
        SPProgressBarPlug.incrementBar();
        INI ini = null;

        //Sets up the file reader for the ini file.
        try {
            ini = new INI("IncreasedSpawns.ini");
        } catch (IOException ex) {
            Logger.getLogger(NPCPotions.class.getName()).log(Level.SEVERE, null, ex);
        }

        Collection<INI.IniSectionHead> sections = getSectionList();

        ini.addSection(sections);

        try {
            ini.readData();
        } catch (IOException ex) {
            Logger.getLogger(NPCPotions.class.getName()).log(Level.SEVERE, null, ex);
        }

        initializeLists(ini);
    }

    private boolean getValidNPC(NPC_ n) {
        boolean include = false;
        boolean exclude = false;

        String edid = n.getEDID().toUpperCase();

        if (n.get(MajorRecord.MajorFlags.Deleted)) {
            return false;
        }
        if (n.isTemplated() && n.get(NPC_.TemplateFlag.USE_SCRIPTS)) {
            return false;
        }

//        if (npcBlockList.contains(n.getForm())) {
//            return false;
//        }
        if (inclusions.isEmpty()) {
            include = true;
        } else {
            for (String list : inclusions) {
                list = list.toUpperCase();
                if (edid.contains(list)) {
                    include = true;
                    break;
                }
            }
        }
        if (include && !exclusions.isEmpty()) {

            for (String list : exclusions) {
                list = list.toUpperCase();
                if (edid.contains(list)) {
                    exclude = true;
                    break;
                }
            }
        }
        return (include && !exclude);

    }

    private int getNumMaxSpawns(NPC_ n) {
        String list;
        if (!npcMaxSettings.isEmpty()) {
            for (Map.Entry<String, String> entry : npcMaxSettings.entrySet()) {
                list = entry.getKey();
                if (n.getEDID().toUpperCase().contains(list)) {
                    return Integer.parseInt(npcMaxSettings.get(list));
                }
            }
        }
        return 9;
    }

    private float getNumReducedSpawns(NPC_ n) {
        String list;
        if (!npcReducedSettings.isEmpty()) {
            for (Map.Entry<String, String> entry : npcReducedSettings.entrySet()) {
                list = entry.getKey();
                if (n.getEDID().toUpperCase().contains(list)) {
                    return Float.parseFloat(npcReducedSettings.get(list));
                }
            }
        }
//        return -1;
        return 1;
    }

    private float getNumIncreasedSpawns(NPC_ n) {
        String list;
        if (!npcIncreasedSettings.isEmpty()) {
            for (Map.Entry<String, String> entry : npcIncreasedSettings.entrySet()) {
                list = entry.getKey();
                if (n.getEDID().toUpperCase().contains(list)) {
                    return Float.parseFloat(npcIncreasedSettings.get(list));
                }
            }
        }
//        return -1;
        return 1;
    }

    private void getBlockList() {
        //Clean-up NPC's from excluded mods.  This is done as blocking the import 
        // could cause previous NPC's to override an excluded mods NPC's.
        String list;
        Mod tempMod;
        for (int i = 0; i < modExclusions.size(); i++) {
            list = (String) modExclusions.get(i);
            try {
                tempMod = SPDatabase.getMod(new ModListing(list));
                for (NPC_ n : tempMod.getNPCs()) {
                    npcBlockList.add(n.getForm());
                }
            } catch (Exception e) {
                //Do nothing, probably doesn't have the mod in the exclusions.
            }
        }
    }

    private Collection<INI.IniSectionHead> getSectionList() {
        Collection<INI.IniSectionHead> sectionList = new ArrayList<>();

        for (IniSection currentSection : IniSection.values()) {
            sectionList.add(new INI.IniSectionHead(currentSection.getName(), currentSection.getFormat()));
        }

        return sectionList;
    }

    private void initializeLists(INI ini) {
        inclusions = (ArrayList) ini.getCollection(new INI.IniSectionHead(IniSection.NPCINCLUSIONS.getName(),
                IniSection.NPCINCLUSIONS.getFormat()));
        exclusions = (ArrayList) ini.getCollection(new INI.IniSectionHead(IniSection.NPCEXCLUSIONS.getName(),
                IniSection.NPCEXCLUSIONS.getFormat()));
        modExclusions.addAll((ArrayList) ini.getCollection(new INI.IniSectionHead(IniSection.MODEXCLUSIONS.getName(),
                IniSection.MODEXCLUSIONS.getFormat())));

        patchSettingsMap = ini.getMap(new INI.IniSectionHead(IniSection.PATCHSETTINGS.getName(),
                IniSection.PATCHSETTINGS.getFormat()));

        cellExclusions = (ArrayList) ini.getCollectionForms(new INI.IniSectionHead(IniSection.CELLEXCLUSIONS.getName(),
                IniSection.CELLEXCLUSIONS.getFormat()));

        npcMaxSettings = ini.getMap(new INI.IniSectionHead(IniSection.NPCMAXSPAWNSETTINGS.getName(),
                IniSection.NPCMAXSPAWNSETTINGS.getFormat()));

        npcReducedSettings = ini.getMap(new INI.IniSectionHead(IniSection.NPCREDUCEDSPAWNSETTINGS.getName(),
                IniSection.NPCREDUCEDSPAWNSETTINGS.getFormat()));

        npcIncreasedSettings = ini.getMap(new INI.IniSectionHead(IniSection.NPCREDUCEDSPAWNSETTINGS.getName(),
                IniSection.NPCREDUCEDSPAWNSETTINGS.getFormat()));

        spawnweight[0] = Float.parseFloat(ASIS.save.getStr(GUISettings.ISSPAWN0WEIGHT));
        spawnweight[1] = Float.parseFloat(ASIS.save.getStr(GUISettings.ISSPAWN1WEIGHT));
        spawnweight[2] = Float.parseFloat(ASIS.save.getStr(GUISettings.ISSPAWN2WEIGHT));
        spawnweight[3] = Float.parseFloat(ASIS.save.getStr(GUISettings.ISSPAWN3WEIGHT));
        spawnweight[4] = Float.parseFloat(ASIS.save.getStr(GUISettings.ISSPAWN4WEIGHT));
        spawnweight[5] = Float.parseFloat(ASIS.save.getStr(GUISettings.ISSPAWN5WEIGHT));
        spawnweight[6] = Float.parseFloat(ASIS.save.getStr(GUISettings.ISSPAWN6WEIGHT));
        spawnweight[7] = Float.parseFloat(ASIS.save.getStr(GUISettings.ISSPAWN7WEIGHT));
        spawnweight[8] = Float.parseFloat(ASIS.save.getStr(GUISettings.ISSPAWN8WEIGHT));
        spawnweight[9] = Float.parseFloat(ASIS.save.getStr(GUISettings.ISSPAWN9WEIGHT));

    }

    private enum IniSection {

        PATCHSETTINGS(INI.IniDataFormat.KEY_VALUE, "PATCHSETTINGS"),
        NPCINCLUSIONS(INI.IniDataFormat.VALUE, "NPCINCLUSIONS"),
        NPCEXCLUSIONS(INI.IniDataFormat.VALUE, "NPCEXCLUSIONS"),
        NPCMAXSPAWNSETTINGS(INI.IniDataFormat.KEY_VALUE, "NPCMAXSPAWNSETTINGS"),
        NPCREDUCEDSPAWNSETTINGS(INI.IniDataFormat.KEY_VALUE, "NPCREDUCEDSPAWNSETTINGS"),
        CELLEXCLUSIONS(INI.IniDataFormat.VALUE, "CELLEXCLUSIONS"),
        MODEXCLUSIONS(INI.IniDataFormat.VALUE, "MODEXCLUSIONS");
        private INI.IniDataFormat format;
        private String name;

        IniSection(INI.IniDataFormat format, String name) {
            this.format = format;
            this.name = name;
        }

        String getName() {
            return name;
        }

        INI.IniDataFormat getFormat() {
            return format;
        }
    }
//    String npcType = null;
//    String npcSpawnCount = null;
//    String settingType = null;
//    String settingValue = null;
    Map<String, String> npcMaxSettings = new HashMap<>();
    Map<String, String> npcReducedSettings = new HashMap<>();
    Map<String, String> npcIncreasedSettings = new HashMap<>();
    Map<String, String> patchSettingsMap = new HashMap<>();
//    Map<FormID, FormID> formsOrigDup = new HashMap<>();
    ArrayList<String> inclusions = new ArrayList<>(0);
    ArrayList<String> exclusions = new ArrayList<>(0);
    ArrayList<String> modExclusions = new ArrayList<>(0);
    ArrayList cellExclusions = new ArrayList<>(0);
    ArrayList<FormID> npcBlockList = new ArrayList<>();
    float[] spawnweight = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    boolean reducedInteriorSpawns = false;
    public int numProgressSections = 5;
    private Set<NPC_> validNPCs;
}
